package com.duniptech.engine.core.simulation;

import com.duniptech.engine.core.modeling.Message;
import com.duniptech.engine.core.modeling.api.IAtomic;
import com.duniptech.engine.core.modeling.api.IMessage;
import com.duniptech.engine.core.simulation.api.ICoordinator;
import com.duniptech.engine.core.simulation.api.ISimulator;

public class Simulator implements ISimulator {	
	private boolean debug = Boolean.FALSE;
	
	/** Parent coordinator */
	protected ICoordinator parent;
	/** The atomic model. */
	protected IAtomic model;
	/** Input */
	protected IMessage input;
	/** Output */
	protected IMessage output;
	/** Time of last event. */
	protected double tL;
	/** Time of next event. */
	protected double tN;
	
	public Simulator(IAtomic model) {
		this(model, false);		
	}
	
	public Simulator(IAtomic model, boolean debug){
		parent = null;
		this.model = model;
		input = new Message();
		output = new Message();
		this.model.setSimulator(this);
		if(debug){
			this.debug = debug;
			if(model instanceof IAtomic)
				System.out.println("Creating simulator for: "+model.getName());
		}
	}
	
	public void initialize(double t) {
		tL = t;
		tN = t + ta(t);
	}
	
	public double ta(double t) {
		return model.ta();
	}
	
	public void deltfcn(double t) {
		if (input.isEmpty() && t!=tN) return;
		else if(!input.isEmpty() && t==tN) {
			double e = t - tL;
			model.deltcon(e,input);
		}
		else if(input.isEmpty() && t==tN) {
			model.deltint();
		}
		else if(!input.isEmpty() && t!=tN) {
			double e = t - tL;
			model.deltext(e,input);
		}
		tL = t;
		tN = tL + model.ta();	
		input = new Message();
	}
	
	public void lambda(double t) {
		if(t!=tN) output = new Message();
		else {
			output = model.lambda();
			if(output==null) output = new Message();
		}
	}

	// GETS AND SETS
	
	public void setTL(double tL) { this.tL = tL; }
	
	public void setTN(double tN) { this.tN = tN; }
	
	public double getTL() { return tL; }
	
	public double getTN() { return tN; }
	
	public IAtomic getModel() { return model; }
	
	public void setParent(ICoordinator parent) { this.parent = parent; }
	
	public ICoordinator getParent() { return parent; }
	
	public IMessage getInput() { return input; }
	
	public IMessage getOutput() { return output; }

	public void simInject(double e, IMessage message) {

		double t = tL + e;
		if (e <= model.ta()) {
			input = message;
			deltfcn(t);
			System.out.println("Time: " + t + " ,input injected: ");
			message.print();
			model.logState();
		} else
			System.out.println("<ERROR> Time: " + tL + " ,input rejected : elapsed time " + e + " is not in range.");

	}

	public void simInject(double e, String portName, Object value) {
		IMessage message = new Message();
		message.add(portName, value);
		simInject(e,message);		
	}
}
